/*
 * ctimer.c
 *
 *  Created on: 2017-4-8
 *      Author: chenshisheng
 */

#include "ctimer.h"
#include "list.h"
#include "utils.h"
//#include "active.h"

#include <stdint.h>

#define CTIMER_FLAG_INITIALIZED (0x01)
#define CTIMER_FLAG_STOPED      (0x01 << 1)

#define STIMER_FREQ 100UL
#define TIMx TIM7
#define COUNTER_CLK 200000UL

struct _Shot
{
    int data;
    Ctimer_Callback_t callback; // 软件定时器的回调函数
};

// 软件定时器链表
LIST(_list);

static void _TimInit(void);

void Ctimer_Init(void)
{
    list_init( _list);
    _TimInit();
}

void Ctimer_DeInit(void)
{
    LL_TIM_DisableCounter(TIMx);
    LL_APB1_GRP1_DisableClock(LL_APB1_GRP1_PERIPH_TIM7);
    NVIC_DisableIRQ(TIM7_IRQn);
}

static void _TimInit(void)
{
      LL_TIM_InitTypeDef TIM_InitStruct = {0};

      /* Peripheral clock enable */
      LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM7);

      /* TIM7 interrupt Init */
      NVIC_SetPriority(TIM7_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),2, 0));
      NVIC_EnableIRQ(TIM7_IRQn);

      TIM_InitStruct.Prescaler = (uint16_t)((SystemCoreClock / (2 * COUNTER_CLK)) - 1);
      TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP;
      TIM_InitStruct.Autoreload = (uint16_t)((COUNTER_CLK / STIMER_FREQ) - 1);
      LL_TIM_Init(TIMx, &TIM_InitStruct);

      LL_TIM_ClearFlag_UPDATE(TIMx);
      LL_TIM_EnableIT_UPDATE(TIMx);

      LL_TIM_SetCounter(TIMx, 0);
      LL_TIM_EnableCounter(TIMx);
}

void TIM7_IRQHandler(void)
{
    if(LL_TIM_IsActiveFlag_UPDATE(TIMx))
    {
        LL_TIM_ClearFlag_UPDATE(TIMx);
        Ctimer_TickUpdate(1000 / STIMER_FREQ);
    }
}

/**
 * @brief 硬件定时器的一次中断为一次Tick，每个Tick都递增链表内的软件定时器计数，
 *        若计数到达目标计数值就将其回调函数发到消息队列，交由消息队列服务函数在
 *        main()函数内执行回调函数(在Ctimer_Fire()函数内执行).
 */
void Ctimer_TickUpdate(unsigned int ms)
{
    Ctimer_t *timer, *curTimer;
    struct _Shot shot;

    if(ms == 0)
    {
        return;
    }

    timer = list_head(_list);
//    if(timer == NULL)
//    {
//        Active_Set(Active_Ctimer, false);
//        return;
//    }

    while(timer != NULL)
    {
        curTimer = timer;
        timer = list_item_next(timer);

        if(curTimer->flags & CTIMER_FLAG_STOPED)
        {
            list_remove(_list, curTimer);
            continue;
        }

        curTimer->counter += ms;
        if(curTimer->counter >= curTimer->interval)
        {
            curTimer->counter = 0;

            if(curTimer->flags & CTIMER_FLAG_ONCE)
            {
                Ctimer_Stop(curTimer);
            }

            if(curTimer->flags & CTIMER_FLAG_URGEN)
            {
                curTimer->callback(curTimer->data);
            }
            else
            {
                shot.data = curTimer->data;
                shot.callback = curTimer->callback;
                MsgQueue_Send(MsgQueue_Id_CtimerFire, & shot, sizeof(shot));
            }
        }
    }
}

void Ctimer_Fire(MsgQueue_Msg_t *msg)
{
    struct _Shot *pshot;

    pshot = (struct _Shot *) msg->data;
    pshot->callback(pshot->data);
}

/**
 * @brief 启动一个软件定时器，要求必须使用全局型的定时器变量，
 *        启动后回调函数将被循环触发
 * @param timer 指向全局型的定时器变量
 * @param intervalMs 定时间隔，单位毫秒
 * @param cb    定时到达后被调用的回调函数
 * @param flags 标志集
 * @return 0  启动成功
 *         -1 失败
 */
int Ctimer_Start(Ctimer_t *timer,
        unsigned int intervalMs,
        Ctimer_Callback_t cb,
        unsigned int flags,
        int data)
{

//    if(! Utils_IsGlobalVariable(timer))
//    {
//        return -1;
//    }
    ENTER_CRITICAL_SECTION();
    timer->counter = 0;
    timer->interval = intervalMs;
    timer->flags = flags | CTIMER_FLAG_INITIALIZED;
    timer->data = data;
    timer->callback = cb;

    list_add(_list, timer);
    EXIT_CRITICAL_SECTION();

//    Active_Set(Active_Ctimer, true);
    return 0;
}

#if 0
int Ctimer_Restart(Ctimer_t *timer, unsigned int intervalMs)
{
    if(! (timer->flags & CTIMER_FLAG_INITIALIZED))
    {
        return -1;
    }

    timer->counter = 0;
    timer->interval = intervalMs;
    if(timer->flags & CTIMER_FLAG_STOPED)
    {
        BIT_CLR(timer->flags, CTIMER_FLAG_STOPED);
        list_add(_list, timer);
    }

    return 0;
}
#endif

int Ctimer_Reset(Ctimer_t *timer)
{

    if((! (timer->flags & CTIMER_FLAG_INITIALIZED)) ||
            (timer->flags & CTIMER_FLAG_STOPED))
    {
        return -1;
    }

    timer->counter = 0;
    return 0;
}

int Ctimer_Stop(Ctimer_t *timer)
{
    if(!(timer->flags & CTIMER_FLAG_INITIALIZED))
    {
        return -1;
    }

//    list_remove(_list, timer);
    BIT_SET(timer->flags, CTIMER_FLAG_STOPED);

    return 0;
}

int Ctimer_IsRunning(Ctimer_t *timer)
{
    return (timer->flags & CTIMER_FLAG_INITIALIZED) &&
            ! (timer->flags & CTIMER_FLAG_STOPED);
}

unsigned int Ctimer_MiniTick(void)
{
    Ctimer_t *timer, *curTimer;
    unsigned int t, tick = UINT32_MAX;

    timer = list_head(_list);
    while(timer != NULL)
    {
        curTimer = timer;
        timer = list_item_next(timer);
        if(curTimer->flags & CTIMER_FLAG_STOPED)
        {
            continue;
        }

        t = curTimer->interval - curTimer->counter;
        if(t < tick)
        {
            tick = t;
        }
    }

    if(tick == UINT32_MAX)
    {
        tick = 0;
    }

    return tick;
}
